<!--
 * @Description: 左右分栏组件
 * @Author: HuiSir<https://gitee.com/huisir001>
 * @Date: 2025-09-24 16:22:55
 * @LastEditTime: 2025-09-28 09:47:04
-->
<template>
  <div class="split-box" ref="containerRef">
    <!-- 左侧面板 -->
    <div
      class="left-panel"
      :class="{ collapsed: isLeftCollapsed }"
      :style="{ width: windowWidth >= 768 ? leftWidth : '' }"
    >
      <div class="left-header">
        <div class="left-header-title">{{ leftTitle }}</div>
        <div class="left-header-btn">
          <el-tooltip
            v-if="showSearchBtn"
            :disabled="!searchBtnTip"
            effect="dark"
            :content="searchBtnTip"
            placement="top"
            :auto-close="1000"
          >
            <div class="header-btn" @click="toggleSearchInput">
              <fy-icon name="Search" size="16"></fy-icon>
            </div>
          </el-tooltip>
          <el-tooltip
            v-if="showRefreshBtn"
            :disabled="!refreshBtnTip"
            effect="dark"
            :content="refreshBtnTip"
            placement="top"
            :auto-close="1000"
          >
            <div class="header-btn" @click="emit('refresh', $event)">
              <fy-icon name="Refresh" size="16"></fy-icon>
            </div>
          </el-tooltip>
          <el-tooltip
            v-if="showAddBtn"
            :disabled="!addBtnTip"
            effect="dark"
            :content="addBtnTip"
            placement="top"
            :auto-close="1000"
          >
            <div class="header-btn" @click="emit('add', $event)">
              <fy-icon name="CirclePlus" size="16"></fy-icon>
            </div>
          </el-tooltip>
          <!-- 自定义左侧头按钮 -->
          <slot name="leftHeaderBtn" />
          <div
            v-if="collapsible"
            class="header-btn close-btn"
            @click="closeLeftPanelForSmallScreen"
          >
            <fy-icon name="Close" size="16"></fy-icon>
          </div>
        </div>
      </div>
      <div class="left-content">
        <div class="search-input-wrapper" v-if="showSearchInput">
          <el-input
            v-model="searchVal"
            clearable
            placeholder="输入关键词搜索"
            @change="emit('search', $event)"
          >
            <template #prefix>
              <fy-icon name="Search" size="16" />
            </template>
          </el-input>
        </div>
        <slot name="left"></slot>
      </div>
    </div>

    <!-- 分割线 -->
    <div
      v-if="!isLeftCollapsed"
      class="resize-line"
      :style="{ left: leftWidth }"
      @mousedown="startResize"
      @touchstart="startResize"
    >
      <div class="resize-icon">||</div>
    </div>

    <!-- 右侧面板 -->
    <div
      class="right-panel"
      :class="{ expanded: isLeftCollapsed }"
      :style="{
        width:
          windowWidth >= 768
            ? isLeftCollapsed
              ? '100%'
              : `calc(100% - ${leftWidth})`
            : '100%',
      }"
    >
      <div v-if="collapsible" class="right-header">
        <div class="header-btn arrow-btn" @click="toggleLeftPanel">
          <fy-icon
            :name="isLeftCollapsed ? 'ArrowRight' : 'ArrowLeft'"
            size="16"
          ></fy-icon>
        </div>
        <div class="right-header-title">{{ rightTitle }}</div>
      </div>
      <div class="right-content">
        <slot name="right"></slot>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, nextTick, watch } from "vue";

defineOptions({ name: "FySplitBox" });

// 组件属性
const props = defineProps({
  initialLeftWidth: {
    type: String,
    default: "300px",
    validator: (val) => {
      if (typeof val !== "string") return false;
      // 匹配百分比格式 (e.g. '30%') 或像素格式 (e.g. '300px')
      return (
        /^\d+(\.\d+)?%(?:\s*px)?$/.test(val) ||
        /^\d+(\.\d+)?px(?:\s*%)?$/.test(val) ||
        /^\d+(\.\d+)?$/.test(val)
      );
    },
  },
  minLeftWidth: {
    type: String,
    default: "200px",
    validator: (val) => {
      if (typeof val !== "string") return false;
      return (
        /^\d+(\.\d+)?%(?:\s*px)?$/.test(val) ||
        /^\d+(\.\d+)?px(?:\s*%)?$/.test(val) ||
        /^\d+(\.\d+)?$/.test(val)
      );
    },
  },
  maxLeftWidth: {
    type: String,
    default: "50%",
    validator: (val) => {
      if (typeof val !== "string") return false;
      return (
        /^\d+(\.\d+)?%(?:\s*px)?$/.test(val) ||
        /^\d+(\.\d+)?px(?:\s*%)?$/.test(val) ||
        /^\d+(\.\d+)?$/.test(val)
      );
    },
  },
  // 是否允许展开收起左侧栏
  collapsible: { type: Boolean, default: true },
  leftTitle: { type: String, default: "左侧栏标题" },
  rightTitle: { type: String, default: "右侧栏标题" },
  // 控制左侧面板按钮显示
  showSearchBtn: { type: Boolean, default: true },
  showRefreshBtn: { type: Boolean, default: true },
  showAddBtn: { type: Boolean, default: true },
  searchBtnTip: { type: String, default: "" },
  refreshBtnTip: { type: String, default: "刷新" },
  addBtnTip: { type: String, default: "新增" },
});

const emit = defineEmits(["search", "refresh", "add", "collapse", "resize"]);

// 响应式数据
const windowWidth = ref(window.innerWidth);
const leftWidth = ref(props.initialLeftWidth);
const searchVal = ref("");
const showSearchInput = ref(false);

// 解析宽度值函数
const parseWidth = (widthStr) => {
  if (!widthStr) return { value: 0, unit: "%" };

  // 处理纯数字（默认百分比）
  if (/^\d+(\.\d+)?$/.test(widthStr)) {
    return { value: parseFloat(widthStr), unit: "%" };
  }

  // 处理百分比
  if (/%/.test(widthStr)) {
    const match = widthStr.match(/^(\d+(\.\d+)?)%/);
    return match
      ? { value: parseFloat(match[1]), unit: "%" }
      : { value: 0, unit: "%" };
  }

  // 处理像素
  if (/px/.test(widthStr)) {
    const match = widthStr.match(/^(\d+(\.\d+)?)px/);
    return match
      ? { value: parseFloat(match[1]), unit: "px" }
      : { value: 0, unit: "px" };
  }

  return { value: 0, unit: "%" };
};

// 获取有效的宽度值（考虑容器宽度）
const getValidWidth = (widthStr) => {
  const { value, unit } = parseWidth(widthStr);
  const container = containerRef.value;

  if (unit === "px" && container) {
    const containerWidth = container.getBoundingClientRect().width;
    return (value / containerWidth) * 100;
  }

  return value;
};

// 获取像素值（从百分比）
const getPixelValue = (percent, containerWidth) => {
  return (percent / 100) * containerWidth;
};
const isLeftCollapsed = ref(false);
const isResizing = ref(false);
const containerRef = ref(null);

// 监听侧边缩放
watch(
  () => isLeftCollapsed.value,
  (newVal) => {
    emit("collapse", newVal);
  }
);

/**
 * 切换左侧栏显示/隐藏
 */
const toggleLeftPanel = async () => {
  if (!props.collapsible) return;
  isLeftCollapsed.value = !isLeftCollapsed.value;
  await nextTick();
};

/**
 * 小屏模式切换左侧栏关闭
 */
const closeLeftPanelForSmallScreen = async () => {
  if (window.innerWidth > 768 || !props.collapsible) return;
  isLeftCollapsed.value = !isLeftCollapsed.value;
  await nextTick();
};

// 切换搜索输入框显隐
const toggleSearchInput = () => {
  showSearchInput.value = !showSearchInput.value;
};

// 开始拖拽调整宽度
const startResize = (e) => {
  if (window.innerWidth <= 768) return;

  e.preventDefault();
  isResizing.value = true;
  document.documentElement.style.cursor = "col-resize";
  document.documentElement.style.userSelect = "none";

  // 添加事件监听
  document.addEventListener("mousemove", handleResize);
  document.addEventListener("mouseup", stopResize);
  document.addEventListener("touchmove", handleResize, { passive: false });
  document.addEventListener("touchend", stopResize);
};

// 处理拖拽逻辑
const handleResize = (e) => {
  if (!isResizing.value || isLeftCollapsed.value) return;

  e.preventDefault();
  const clientX = e.type.includes("touch") ? e.touches[0].clientX : e.clientX;
  const container = containerRef.value;

  if (!container) return;

  const containerRect = container.getBoundingClientRect();
  if (containerRect.width === 0) return;

  const newWidth = ((clientX - containerRect.left) / containerRect.width) * 100;

  // 获取最小和最大宽度的百分比值
  const minWidth = getValidWidth(props.minLeftWidth);
  const maxWidth = getValidWidth(props.maxLeftWidth);

  // 确保宽度在最小和最大范围内
  const clampedWidth = Math.max(minWidth, Math.min(maxWidth, newWidth));

  // 根据初始单位决定使用百分比还是像素
  const initialUnit = parseWidth(props.initialLeftWidth).unit;
  if (initialUnit === "px" && containerRect.width > 0) {
    // 如果初始单位是px，将百分比转换回px
    const pixelValue = getPixelValue(clampedWidth, containerRect.width);
    leftWidth.value = pixelValue + "px";
  } else {
    leftWidth.value = clampedWidth + "%";
  }

  // 监听左侧栏百分比宽度拖拽变化
  emit("resize", clampedWidth);
};

// 初始化宽度值
const initWidth = () => {
  const initialParsed = parseWidth(props.initialLeftWidth);
  const container = containerRef.value;

  if (initialParsed.unit === "%" || !container) {
    leftWidth.value = initialParsed.value + "%";
  } else {
    // 如果是px单位，转换为百分比以便在拖拽时保持一致
    const containerWidth = container.getBoundingClientRect().width;
    const percentWidth = (initialParsed.value / containerWidth) * 100;
    leftWidth.value = percentWidth + "%";
  }
};

// 停止拖拽
const stopResize = () => {
  isResizing.value = false;
  document.documentElement.style.cursor = "";
  document.documentElement.style.userSelect = "";

  // 清理事件监听
  document.removeEventListener("mousemove", handleResize);
  document.removeEventListener("mouseup", stopResize);
  document.removeEventListener("touchmove", handleResize);
  document.removeEventListener("touchend", stopResize);
};

// 处理窗口大小变化
const handleWindowResize = async () => {
  const newWidth = window.innerWidth;
  windowWidth.value = newWidth;

  // 小屏幕自动折叠，大屏幕自动展开
  if (props.collapsible) {
    if (newWidth < 768 && !isLeftCollapsed.value) {
      isLeftCollapsed.value = true;
      await nextTick();
    } else if (newWidth >= 768 && isLeftCollapsed.value) {
      isLeftCollapsed.value = false;
      await nextTick();

      // 在大屏幕展开时重新计算宽度，确保比例正确
      setTimeout(() => {
        const container = containerRef.value;
        if (container) {
          const initialParsed = parseWidth(props.initialLeftWidth);
          if (initialParsed.unit === "px") {
            // 如果初始单位是px，重新计算百分比
            const containerWidth = container.getBoundingClientRect().width;
            const percentWidth = (initialParsed.value / containerWidth) * 100;
            leftWidth.value = percentWidth + "%";
          }
        }
      }, 0);
    }
  }
};

// 生命周期钩子
onMounted(() => {
  window.addEventListener("resize", handleWindowResize);
  handleWindowResize(); // 初始化检查

  // 等待容器渲染完成后初始化宽度
  setTimeout(() => {
    initWidth();
  }, 0);
});

onUnmounted(() => {
  window.removeEventListener("resize", handleWindowResize);
  // 确保清理所有事件监听
  document.removeEventListener("mousemove", handleResize);
  document.removeEventListener("mouseup", stopResize);
  document.removeEventListener("touchmove", handleResize);
  document.removeEventListener("touchend", stopResize);
});

defineExpose({
  toggleLeftPanel,
  closeLeftPanelForSmallScreen,
  toggleSearchInput,
});
</script>

<style lang="scss" scoped>
@use "../../styles/mixin.scss" as *;
/* 主容器 */
.split-box {
  display: flex;
  height: 100%;
  min-height: 0; /* 解决100%高度计算问题 */
  position: relative;
  overflow: hidden;
  background-color: #ffffff;

  /* 左右面板通用样式 */
  .left-panel,
  .right-panel {
    height: 100%;
    background: #ffffff;
    overflow: hidden;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    min-height: 0;
    transition: all 0.5s cubic-bezier(0.25, 0.8, 0.25, 1);
  }

  .left-content,
  .right-content {
    position: relative;
    flex: 1;
    min-height: 0;
    overflow: auto;
    box-sizing: border-box;
    padding: 10px;
    @include scrollBar;
  }

  .search-input-wrapper {
    position: sticky;
    top: 0;
    z-index: 10;
    padding: 10px 0 8px;
    background-color: #ffffff;
    margin-top: -10px;
    transform: translateY(-10px);
  }

  /* 左侧面板特有样式 */
  .left-panel {
    flex-shrink: 0;
    border-right: var(--el-border);
    z-index: 2;
  }

  /* 右侧面板特有样式 */
  .right-panel {
    flex: 1;
    position: relative;
    z-index: 1;

    /* 右侧面板展开状态 */
    &.expanded {
      width: 100% !important;
      z-index: 2;
    }
  }
}

.left-header,
.right-header {
  height: 42px;
  line-height: 22px;
  color: var(--el-text-color-primary);
  border-bottom: var(--el-border);
  padding: 9px 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  .header-btn {
    width: 24px;
    height: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 2px;
    cursor: pointer;
    transition: all 0.3s;
    &.arrow-btn {
      background-color: var(--el-fill-color-light);
    }
    &:hover {
      background-color: var(--el-fill-color);
    }
    &:active {
      background-color: var(--el-color-primary-light-7);
    }
  }
}
.left-header {
  .header-btn + .header-btn {
    margin-left: 5px;
  }
  .close-btn {
    display: none;
  }
}
.right-header-title {
  width: calc(100% - 35px);
  padding-right: 35px;
  text-align: center;
}

/* 分割线 */
.resize-line {
  position: absolute;
  top: 0;
  width: 6px;
  height: 100%;
  background: transparent;
  cursor: col-resize;
  z-index: 10;
  transition: background-color 0.2s ease;

  &:hover {
    background-color: var(--el-fill-color-lighter);

    .resize-icon {
      opacity: 1;
    }
  }

  &:active {
    background-color: var(--el-fill-color);
  }

  .resize-icon {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 10px;
    color: #999;
    opacity: 0;
    transition: opacity 0.2s ease;
  }
}

/* 桌面版样式 (≥769px) */
@media (min-width: 769px) {
  .split-box {
    .left-panel {
      &.collapsed {
        width: 0 !important;
        position: absolute;
        top: 0;
        left: 0;
        z-index: 0;
        transform: translateX(-100%);
        opacity: 0;

        .left-content {
          transform: translateX(-20px);
          opacity: 0;
        }
      }
    }
  }
}

/* 小屏幕样式 (≤768px) */
@media (max-width: 768px) {
  .split-box {
    /* 左右面板定位 */
    .left-panel,
    .right-panel {
      position: absolute;
      top: 0;
      left: 0;
    }

    /* 右侧面板始终在背景层 */
    .right-panel {
      z-index: 1;
      width: 100% !important;
    }

    /* 左侧面板滑动效果 */
    .left-panel {
      z-index: 2;
      width: 100% !important;
      border-right: none !important;
      &.collapsed {
        transform: translateX(-100%);
        z-index: 2;
        opacity: 1 !important;
      }
    }
  }

  /* 隐藏分割线 */
  .resize-line {
    display: none !important;
  }

  /* 小屏左侧面板关闭按钮 */
  .left-header {
    .close-btn {
      display: inline-flex;
    }
  }
}
</style>
